package com.xzzz.expand.gateway.application.filter;

import cn.hutool.core.collection.CollUtil;
import com.xzzz.irda.tracker.core.adapter.gateway.TrackerGlobalFilter;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * @author xzzz
 */
@Slf4j
@Component
public class RequestLogGlobalFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();


        HttpMethod method = request.getMethod();
        Route gatewayUrl = exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);

        String traceId = exchange.getAttribute(TrackerGlobalFilter.TRACKER_TRACE_ID);
        String spanId = exchange.getAttribute(TrackerGlobalFilter.TRACKER_SPAN_ID);

        MDC.put("traceId", traceId);
        MDC.put("spanId", spanId);

        try {
            // 请求参数
            StringBuilder queryParams = new StringBuilder();
            for (Map.Entry<String, List<String>> entry : exchange.getRequest().getQueryParams().entrySet()) {
                queryParams.append("[").append(entry.getKey()).append(" : ").append(entry.getValue()).append("] ");
            }

            if (method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH || method == HttpMethod.DELETE) {
                // 请求体参数
                return DataBufferUtils.join(request.getBody())
                        .defaultIfEmpty(exchange.getResponse().bufferFactory().wrap("{}".getBytes()))
                        .flatMap(dataBuffer -> {
                            MDC.put("traceId", traceId);
                            MDC.put("spanId", spanId);

                            try {
                                log.debug("==================== 请求体参数 [HttpRequestBodyFilter] ==================== ");
                                log.debug("{} : {} : to {}", method, request.getURI().toString(), gatewayUrl.getUri());
                                log.debug("请求参数 : {}", queryParams.toString());

                                // 判断请求头, 只有 json 请求体才输出
                                List<String> contentTypes = request.getHeaders().get(HttpHeaders.CONTENT_TYPE);

                                boolean isJsonBody = false;
                                if (CollUtil.isNotEmpty(contentTypes)) {
                                    for (String contentType : contentTypes) {
                                        if (contentType.contains(MediaType.APPLICATION_JSON_VALUE) ||
                                                contentType.contains(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
                                            isJsonBody = true;
                                            break;
                                        }
                                    }
                                }
                                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                                dataBuffer.read(bytes);

                                if (isJsonBody) {
                                    String bodyStr = new String(bytes, StandardCharsets.UTF_8);
                                    exchange.getAttributes().put("POST_BODY", bodyStr);
                                    log.debug("请求体参数 : {}", bodyStr);
                                } else {
                                    log.debug("请求体参数 : 非 application/json 请求, 不打印请求体参数");
                                }

                                DataBufferUtils.release(dataBuffer);
                                Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
                                    DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes);
                                    return Mono.just(buffer);
                                });
                                ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
                                    @Override
                                    public Flux<DataBuffer> getBody() {
                                        return cachedFlux;
                                    }
                                };
                                return chain.filter(exchange.mutate().request(mutatedRequest).build());
                            } finally {
                                MDC.remove("traceId");
                                MDC.remove("spanId");
                            }

                        });
            } else {
                log.debug("==================== 请求体参数 [HttpRequestBodyFilter] ==================== ");
                log.debug("{} : {} : to {}", method, request.getURI().toString(), gatewayUrl.getUri());
                log.debug("请求参数 : {}", queryParams.toString());
            }

            return chain.filter(exchange);
        } finally {
            MDC.remove("traceId");
            MDC.remove("spanId");
        }
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
